﻿//-----------------------------------------------------------------------
// <Copyright>
// * Copyright (C) 2022 RuYiAdmin All Rights Reserved
// </Copyright>
//-----------------------------------------------------------------------

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using RuYiAdmin.Net.CommonInfrastructure.Classes;
using RuYiAdmin.Net.CommonInfrastructure.Configurations;
using RuYiAdmin.Net.CommonInfrastructure.Constants.Framework;
using RuYiAdmin.Net.CommonInfrastructure.Constants.System;
using RuYiAdmin.Net.CommonInfrastructure.Enums.Business;
using RuYiAdmin.Net.CommonInfrastructure.Utilities.Contexts;
using RuYiAdmin.Net.CommonInfrastructure.Utilities.Utils;
using RuYiAdmin.Net.EntityDataModel.DataTransformationModel.SystemModel;
using RuYiAdmin.Net.EntityDataModel.EntityModel.SystemModel;
using RuYiAdmin.Net.ServiceLayer.BusinessServiceExtension;
using System;

namespace RuYiAdmin.Net.WebApi.RuYiAdminCode.RuYiAdminFilter
{
    [AttributeUsage(AttributeTargets.Class)]
    public class ActionAuthorization : ActionFilterAttribute
    {
        /// <summary>
        /// 系统前置鉴权
        /// </summary>
        /// <param name="context">ActionExecutingContext</param>
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            #region 系统前置鉴权

            #region 处理匿名方法

            foreach (var item in context.ActionDescriptor.EndpointMetadata)
            {
                if (item.GetType().Name.Equals(typeof(AllowAnonymousAttribute).Name))
                {
                    return;
                }
            }

            #endregion

            #region 处理白名单

            var whiteList = RuYiGlobalConfig.SystemConfig.WhiteList;
            if (!string.IsNullOrEmpty(whiteList))
            {
                var array = whiteList.Split(',');
                var url = context.HttpContext.Request.Path.Value;
                foreach (var item in array)
                {
                    if (url.EndsWith(item))
                    {
                        return;
                    }
                }
            }

            #endregion

            #region 用户Jwt验证

            if (RuYiGlobalConfig.SystemConfig.CheckJwtToken)
            {
                if (!context.HttpContext.Request.Headers.ContainsKey(Keywords.AUTHORIZATION))
                {
                    context.Result = new UnauthorizedObjectResult(ExceptionMessage.UnauthorizedJwtTokenExceptionMessage);
                    return;
                }
            }

            #endregion

            #region 用户Token验证

            if (RuYiGlobalConfig.SystemConfig.CheckToken)
            {
                if (!context.HttpContext.Request.Headers.ContainsKey(Keywords.TOKEN))
                {
                    context.Result = new UnauthorizedObjectResult(ExceptionMessage.NeccesarryTokenExceptionMessage);
                    return;
                }
                else
                {
                    #region 用户token续时操作

                    //获取用户token
                    var token = context.HttpContext.GetToken();
                    //获取用户
                    var user = RuYiRedisContext.Get<SysUserDTO>(token);
                    //用户token合法
                    if (user != null)
                    {
                        string salt = context.HttpContext.GetTokenSalt();
                        Guid id = Guid.Parse(salt);

                        if (id == Guid.Empty)
                        {
                            //处理非法请求
                            context.Result = new BadRequestObjectResult(WarnningMessage.AccessDeniedMessage);
                            return;
                        }

                        var entity = RuYiAdminDbScope.RuYiDbContext.Queryable<SysRecord>().Where(t => t.Id == id).First();
                        if (entity == null)
                        {
                            #region 记录合法salt

                            SysRecord record = new SysRecord();
                            record.Id = id;
                            record.Remark = "1";
                            record.Creator = user.Id;
                            record.CreateTime = DateTime.Now;
                            record.Modifier = user.Id;
                            record.ModifyTime = DateTime.Now;
                            record.VersionId = Guid.NewGuid();
                            RuYiAdminDbScope.RuYiDbContext.Insertable(record).ExecuteCommand();

                            #endregion

                            #region 用户token续时

                            var tokenExpiration = RuYiGlobalConfig.SystemConfig.UserTokenExpiration * 60;
                            RuYiRedisContext.Expire(token, tokenExpiration);

                            #endregion
                        }
                        else
                        {
                            #region 记录Token劫持行为

                            #region Token劫持记录审计日志

                            var log = SysLogServiceExtension.GetSysLog(context.HttpContext);

                            //字段赋值
                            log.OperationType = OperationType.TokenHijacked;
                            log.Remark = $"用户{string.Join("/", user.LogonName, user.DisplayName)}的Token被劫持、正在进行接口渗透，" +
                                $"使用口令为{token}。该口令已经作废，请管理员警惕、关注！";

                            log.Write();

                            #endregion

                            #region 发送Token劫持告警消息

                            var msg = new SystemMessage();

                            msg.Message = Keywords.BROADCAST;
                            msg.MessageType = MessageType.Broadcast;

                            BroadcastMessage broadcastMessage = new BroadcastMessage();
                            broadcastMessage.Title = "用户Token劫持告警";
                            broadcastMessage.Message = log.Remark;
                            broadcastMessage.MessageLevel = MessageLevel.Severity;

                            msg.Object = broadcastMessage;

                            RuYiActiveMQContext.SendTopic(JsonConvert.SerializeObject(msg));

                            #endregion

                            #region 强制下线记录审计日志

                            log = SysLogServiceExtension.GetSysLog(context.HttpContext);

                            //字段赋值
                            log.OperationType = OperationType.ForceLogout;
                            log.Remark = $"{user.DisplayName + "/" + user.LogonName}由于token劫持被系统强制下线";

                            log.Write();

                            #endregion

                            //token作废
                            RuYiRedisContext.Delete(new string[] { token });

                            #region 强制用户下线消息

                            msg = new SystemMessage();
                            msg.Message = Keywords.FORCELOGOUT;
                            msg.MessageType = MessageType.ForceLogout;
                            msg.Object = user;

                            RuYiActiveMQContext.SendTopic(JsonConvert.SerializeObject(msg));

                            #endregion

                            #endregion

                            context.Result = new ForbidResult(WarnningMessage.IllegalAccessMessage);
                            return;
                        }
                    }
                    else
                    {
                        //处理非法请求
                        context.Result = new UnauthorizedObjectResult(ExceptionMessage.InvalidTokenExceptionMessage);
                        return;
                    }

                    #endregion
                }
            }

            #endregion

            #region 其他头部验证

            var headerConfig = RuYiGlobalConfig.SystemConfig.HeaderConfig;
            if (!string.IsNullOrEmpty(headerConfig))
            {
                var array = headerConfig.Split(',');
                foreach (var item in array)
                {
                    if (!context.HttpContext.Request.Headers.ContainsKey(item))
                    {
                        context.Result = new BadRequestObjectResult(item.ToLower() + WarnningMessage.NeccesarryItemMessage);
                        return;
                    }
                }
            }

            #endregion

            #endregion
        }
    }
}
